package com.fast.fast.common.gateway.filter;

import cn.dev33.satoken.router.SaHttpMethod;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;

/**
 * 自定义全局过滤器类
 *
 * @author lyf
 * @date 2022/01/01 00:00 周六
 **/
@Slf4j
@Component
public class AccessLogGlobalFilter implements GlobalFilter, Filter {

    private static final String START_TIME = "startTime";

    private static final String X_REAL_IP = "X-Real-IP";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(START_TIME, System.currentTimeMillis());

        return chain
                // 继续调用filter
                .filter(exchange)
                // filter的后置处理
                .then(
                        Mono.fromRunnable(
                                () -> {
                                    Long startTime = exchange.getAttribute(START_TIME);
                                    if (startTime != null) {
                                        Long executeTime = (System.currentTimeMillis() - startTime);

                                        // 解决网关转发到微服务时的跨域问题
                                        exchange.getResponse().getHeaders().entrySet().stream()
                                                .filter(entry -> (entry.getValue() != null && entry.getValue().size() > 1))
                                                .filter(entry -> HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN.equals(entry.getKey()) || HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS.equals(entry.getKey()))
                                                .forEach(entry -> {
                                                    entry.setValue(Collections.singletonList(entry.getValue().get(1)));
                                                });

                                        // 调试模式下输出访问日志
                                        List<String> ips = exchange.getRequest().getHeaders().get(X_REAL_IP);
                                        String ip = ips != null ? ips.get(0) : null;
                                        String host = exchange.getRequest().getURI().getHost();
                                        String method = exchange.getRequest().getMethod().name();
                                        String path = exchange.getRequest().getURI().getRawPath();
                                        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
                                        int code = exchange.getResponse().getStatusCode() == null ? 500 : exchange.getResponse().getStatusCode().value();
                                        log.debug("IP：[{}]，Host：[{}]，Method：[{}]，Path：[{}]，QueryParams：{}，StatusCode：{}，Time：{}ms"
                                                , ip, host, method, path, queryParams, code, executeTime);
                                        log.debug("==================================================================================================================");
                                    }
                                }
                        )
                );
    }

    /**
     * 解决网关转发到微服务时的跨域问题
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     */
    @SneakyThrows
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "*");

        String method = request.getMethod();
        if (SaHttpMethod.OPTIONS.name().equalsIgnoreCase(method)) {
            servletResponse.getOutputStream().write("Success".getBytes(StandardCharsets.UTF_8));
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
}
